package com.bmit.clusterspring.service;

import com.bmit.clusterspring.log.SaveInnerLogThread;
import com.bmit.clusterspring.mapper.InnerLogMapper;
import com.bmit.clusterspring.model.InnerLog;
import com.bmit.clusterspring.util.RateUtils;
import com.bmit.clusterspring.util.ThreadPoolUtils;
import dataset.CustomizedDataset;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import performance.inner.InnerPerformanceMeasure;
import util.CsvUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class InServiceImpl implements InService {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Value("${file.raw}")
    private String rawPath;

    @Value("${file.before}")
    private String beforePath;

    @Autowired
    private LogService logService;

    @Autowired
    private InnerLogMapper mapper;


    @Override
    public boolean uploadCSV(String ipAddr, MultipartFile file) throws IOException {
        String nowNanoTime = String.valueOf(System.nanoTime());
        String token = nowNanoTime.substring(nowNanoTime.length() - 8);
        String oldRaw = (String) redisTemplate.opsForValue().get(ipAddr + "@raw");
        String fileName = token + "%raw%" + file.getOriginalFilename();
        BufferedOutputStream out = new BufferedOutputStream(
                new FileOutputStream(new File(rawPath + fileName)));
        out.write(file.getBytes());
        out.flush();
        out.close();
        redisTemplate.opsForValue().set(ipAddr + "@raw", fileName);
        System.out.println("raw:" + fileName);
        return true;
    }

    @Override
    public List<Map<String, String>> showCSV(String ipAddr) {
        String raw = (String) redisTemplate.opsForValue().get(ipAddr + "@raw");
        if (raw == null) return null;
        List<Map<String, String>> data = CsvUtils.csvToMap(rawPath + raw, ",");
        data = data.size() < 10 ? data : data.subList(0, 10);
        return data;
    }

    @Override
    public void setColumns(String ipAddr, List<String> cols) {
        String fileName = (String) redisTemplate.opsForValue().get(ipAddr + "@raw");
        System.out.println("str:" + fileName);
        String[] split = fileName.split("%");
        String newFileName = split[0] + "%measured%" + split[2];
        CsvUtils.subCsv(rawPath + fileName, beforePath + newFileName, ",", cols);
        deleteFile(rawPath + fileName);
        redisTemplate.opsForValue().set(ipAddr + "@measured", newFileName);
        redisTemplate.opsForValue().set(ipAddr + "@size", String.valueOf(cols.size() - 1));
        System.out.println("str:" + newFileName);
    }

    @Override
    public void runMeasure(String ipAddr) {
        String measuredFileName = (String) redisTemplate.opsForValue().get(ipAddr + "@measured");
        System.out.println("run:" + measuredFileName);
        List<List<String>> dataString = CsvUtils.csvToList(beforePath + measuredFileName, ",");
        deleteFile(beforePath + measuredFileName);
        Integer size = Integer.parseInt((String) redisTemplate.opsForValue().get(ipAddr + "@size"));
        CustomizedDataset ds = new CustomizedDataset(dataString, size, 0);
        InnerPerformanceMeasure measure = new InnerPerformanceMeasure(ds);
        Map<String, Double> measures = measure.measures();
        redisTemplate.opsForHash().put(ipAddr + "@mere", "FILE_NAME", measuredFileName.split("%")[2]);
        redisTemplate.opsForHash().put(ipAddr + "@mere", "DI", String.valueOf(measures.get("DI")));
        redisTemplate.opsForHash().put(ipAddr + "@mere", "DBI", String.valueOf(measures.get("DBI")));
        redisTemplate.opsForHash().put(ipAddr + "@mere", "COST_TIME", String.valueOf(measures.get("COST_TIME")));
        InnerLog log = new InnerLog();
        log.setDbi(measures.get("DBI"));
        log.setDi(measures.get("DI"));
        log.setIpAddress(ipAddr);
        log.setRunTime(measures.get("COST_TIME"));
        log.setGmtCreate(new Date());
        ThreadPoolUtils.getPool().execute(new SaveInnerLogThread(logService, log));
    }

    @Override
    public Map<String, String> getResult(String ipAddr) {
        Map<String, String> result = new HashMap<>();
        Double di = Double.parseDouble((String) redisTemplate.opsForHash().get(ipAddr + "@mere", "DI"));
        Double dbi = Double.parseDouble((String) redisTemplate.opsForHash().get(ipAddr + "@mere", "DBI"));
        List<Double> diList = mapper.getDIs();
        List<Double> dbiList = mapper.getDBIs();
        int diPosition = RateUtils.insertPosition(diList, di, RateUtils.DESC);
        int dbiPosition = RateUtils.insertPosition(dbiList, dbi, RateUtils.ASC);
        result.put("FILE_NAME", (String) redisTemplate.opsForHash().get(ipAddr + "@mere", "FILE_NAME"));
        result.put("TOTAL", String.valueOf(diList.size()));
        result.put("DI", String.valueOf(di));
        result.put("DBI", String.valueOf(dbi));
        result.put("DI_POS", String.valueOf(diPosition));
        result.put("DBI_POS", String.valueOf(dbiPosition));
        result.put("COST_TIME", (String) redisTemplate.opsForHash().get(ipAddr + "@mere", "COST_TIME"));
        return result;
    }

    public boolean deleteFile(String path) {
        return new File(path).delete();
    }
}
